package belf.migrate.engine.ddl.trigger.mysql;

import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.Catalog;
import belf.migrate.api.util.QuoteType;
import belf.migrate.engine.ddl.BaseDDLConvert;
import belf.migrate.engine.ddl.helper.mysql.MySqlAntlrBuilder;
import belf.migrate.plugin.mysql.antlr.MySqlLexer;
import belf.migrate.plugin.mysql.antlr.MySqlParser;
import belf.migrate.plugin.mysql.antlr.MySqlParserBaseListener;
import belf.migrate.plugin.mysql.antlr.MySqlParserBaseVisitor;
import com.github.vertical_blank.sqlformatter.SqlFormatter;
import com.github.vertical_blank.sqlformatter.languages.Dialect;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.StringUtils;

public class MySql2DMTriggerConvert extends BaseDDLConvert {

    public MySql2DMTriggerConvert(String objectName, String sql, Catalog sourceCatalog, Catalog sinkCatalog,
                                  String schema, QualifiersModel qualifiersModel) {
        super(objectName, sql, sourceCatalog, sinkCatalog, schema, qualifiersModel);
    }

    @Override
    public String convert() {
        CharStream input = CharStreams.fromString(sql);

        // 创建词法分析器和解析器
        MySqlLexer lexer = new MySqlLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        MySqlParser parser = new MySqlParser(tokens);

        // 从解析器中获取起始规则并进行解析
        MySqlParser.RootContext tree = parser.root();
        MySqlTriggerVisitor triggerVisitor = new MySqlTriggerVisitor(this);
        triggerVisitor.visit(tree);
        return triggerVisitor.toTriggerDDL();
    }

    private class MySqlTriggerVisitor extends MySqlParserBaseVisitor<String> {
        private BaseDDLConvert ddlConvert;

        private String triggerName;
        private String tableName;

        private MySqlParser.CreateTriggerContext createTriggerContext;
        //mysql的触发器定义包含BEGIN...END的情况，，以这个为根节点进行遍历
        private MySqlParser.ProcedureSqlStatementContext procedureSqlStatemtContext;
        //mysql的触发器定义没有包含BEGIN...END的，以这个为根节点进行遍历
        private MySqlParser.RoutineBodyContext routineBodyContext;

        public MySqlTriggerVisitor(BaseDDLConvert ddlConvert) {
            this.ddlConvert = ddlConvert;
        }

        @Override
        public String visitCreateTrigger(MySqlParser.CreateTriggerContext ctx) {
            this.createTriggerContext = ctx;
            triggerName = createTriggerContext.fullId().get(0).getText().trim();
            triggerName = StringUtils.replace(triggerName, "`", "");
            triggerName = qualifiersModel.getLeftQuote() + triggerName + qualifiersModel.getRightQuote();

            tableName = createTriggerContext.tableName().fullId().getText().trim();
            tableName = StringUtils.replace(tableName, "`", "");
            tableName = qualifiersModel.getLeftQuote() + tableName + qualifiersModel.getRightQuote();

            return visitChildren(ctx);
        }

        @Override
        public String visitRoutineBody(MySqlParser.RoutineBodyContext ctx) {
            this.routineBodyContext = ctx;
            return visitChildren(ctx);
        }

        @Override
        public String visitProcedureSqlStatement(MySqlParser.ProcedureSqlStatementContext ctx) {
            procedureSqlStatemtContext = ctx;
            return visitChildren(ctx);
        }

        public String toTriggerDDL() {
            StringBuilder sb = new StringBuilder();
            int childCount = createTriggerContext.getChildCount();
            for (int i = 0; i < childCount; i++) {
                ParseTree parseTree = createTriggerContext.getChild(i);
                if (parseTree instanceof TerminalNode) {
                    TerminalNode terminalNode = (TerminalNode)parseTree;
                    sb.append(terminalNode.getText() + " ");
                } else if (parseTree instanceof MySqlParser.FullIdContext){
                    if (schema != null && !(triggerName.toUpperCase().contains(schema.toUpperCase()))) {
                        sb.append(qualifiersModel.getLeftQuote() + schema + qualifiersModel.getRightQuote() + ".");
                    }
                    sb.append(triggerName + " ");
                } else if (parseTree instanceof MySqlParser.TableNameContext) {
                    if (schema != null && !(tableName.toUpperCase().contains(schema.toUpperCase()))) {
                        sb.append(qualifiersModel.getLeftQuote() + schema + qualifiersModel.getRightQuote() + ".");
                    }
                    sb.append(tableName + " ");
                }
            }

            //这里定义一个空的StringBuilder，接收BEGIN...END之间的语句
            StringBuilder internalSb = new StringBuilder();
            MySqlAntlrBuilder builder = new MySqlAntlrBuilder(ddlConvert, internalSb, QuoteType.DOUBLE_QUOTE.getValue(), schema);
            MySqlTriggerListener listener = new MySqlTriggerListener(builder);
            ParseTreeWalker walker = new ParseTreeWalker();
            if (procedureSqlStatemtContext != null) {
                walker.walk(listener, procedureSqlStatemtContext);
            } else if (routineBodyContext != null) {
                walker.walk(listener, routineBodyContext);
            }
            sb.append("\nBEGIN\n");
            String sqlBody = listener.getBody();
            if (!sqlBody.endsWith(";")) {
                sqlBody += ";";
            }
            sb.append(sqlBody);
            if (!sqlBody.endsWith(";")) {
                sb.append(";");
            }
            sb.append("\nEND;");

            return SqlFormatter.of(Dialect.PlSql).format(sb.toString()); //达梦数据库的语法兼容Oracle
        }
    }

    private class MySqlTriggerListener extends MySqlParserBaseListener {
        private MySqlAntlrBuilder builder;

        public MySqlTriggerListener(MySqlAntlrBuilder builder) {
            this.builder = builder;
        }

        @Override
        public void visitTerminal(TerminalNode node) {
            if (node.getText().equalsIgnoreCase("NEW")) {
                builder.append(":NEW");
            } else if (node.getText().equalsIgnoreCase("OLD")) {
                builder.append(":OLD");
            } else if (node.getText().equals("=") && node.getParent() instanceof MySqlParser.UpdateStatementContext) {
                builder.append(" := ");
            } else {
                builder.output(node);
            }

        }

        public String getBody() {
            return builder.toSql();
        }
    }
}
